/*******************************************************************************
* Copyright (c) 2010-2012, Zoltan Ujhelyi, Istvan Rath and Daniel Varro
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Mark Czotter, Zoltan Ujhelyi - initial API and implementation
* Andras Okros - new validators added
*******************************************************************************/
package org.eclipse.incquery.patternlanguage.emf.validation;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.incquery.patternlanguage.emf.EMFPatternLanguageScopeHelper;
import org.eclipse.incquery.patternlanguage.emf.ResolutionException;
import org.eclipse.incquery.patternlanguage.emf.eMFPatternLanguage.EMFPatternLanguagePackage;
import org.eclipse.incquery.patternlanguage.emf.eMFPatternLanguage.EnumValue;
import org.eclipse.incquery.patternlanguage.emf.eMFPatternLanguage.PackageImport;
import org.eclipse.incquery.patternlanguage.emf.eMFPatternLanguage.PatternModel;
import org.eclipse.incquery.patternlanguage.emf.helper.EMFPatternLanguageHelper;
import org.eclipse.incquery.patternlanguage.emf.scoping.IMetamodelProvider;
import org.eclipse.incquery.patternlanguage.emf.types.EMFPatternTypeUtil;
import org.eclipse.incquery.patternlanguage.emf.types.IEMFTypeProvider;
import org.eclipse.incquery.patternlanguage.helper.CorePatternLanguageHelper;
import org.eclipse.incquery.patternlanguage.patternLanguage.AggregatedValue;
import org.eclipse.incquery.patternlanguage.patternLanguage.CheckConstraint;
import org.eclipse.incquery.patternlanguage.patternLanguage.CompareConstraint;
import org.eclipse.incquery.patternlanguage.patternLanguage.CompareFeature;
import org.eclipse.incquery.patternlanguage.patternLanguage.ComputationValue;
import org.eclipse.incquery.patternlanguage.patternLanguage.Constraint;
import org.eclipse.incquery.patternlanguage.patternLanguage.LiteralValueReference;
import org.eclipse.incquery.patternlanguage.patternLanguage.PathExpressionConstraint;
import org.eclipse.incquery.patternlanguage.patternLanguage.PathExpressionHead;
import org.eclipse.incquery.patternlanguage.patternLanguage.Pattern;
import org.eclipse.incquery.patternlanguage.patternLanguage.PatternBody;
import org.eclipse.incquery.patternlanguage.patternLanguage.PatternCall;
import org.eclipse.incquery.patternlanguage.patternLanguage.PatternCompositionConstraint;
import org.eclipse.incquery.patternlanguage.patternLanguage.PatternLanguagePackage;
import org.eclipse.incquery.patternlanguage.patternLanguage.ValueReference;
import org.eclipse.incquery.patternlanguage.patternLanguage.Variable;
import org.eclipse.incquery.patternlanguage.patternLanguage.VariableValue;
import org.eclipse.incquery.patternlanguage.validation.UnionFindForVariables;
import org.eclipse.xtext.validation.Check;
import com.google.inject.Inject;
/**
* Validators for EMFPattern Language:
* <ul>
* <li>Duplicate import of EPackages</li>
* <li>Enum types</li>
* <li>Unused variables</li>
* <li>Type checking for parameters and body variables</li>
* <li>Type checking for literal and computational values in pattern calls, path expressions and compare constraints
* <li>Pattern body searching for isolated constraints (cartesian products)</li>
* <li>Non-EDataTypes in check expression</li>
* </ul>
*/
public class EMFPatternLanguageJavaValidator extends AbstractEMFPatternLanguageJavaValidator {
@Inject
private IMetamodelProvider metamodelProvider;
@Inject
private IEMFTypeProvider emfTypeProvider;
@Override
protected List<EPackage> getEPackages() {
// PatternLanguagePackage must be added to the defaults, otherwise the core language validators not used in the
// validation process
List<EPackage> result = super.getEPackages();
result.add(PatternLanguagePackage.eINSTANCE);
return result;
}
@Check
public void checkDuplicatePackageImports(PatternModel patternModel) {
List<PackageImport> importPackages = EMFPatternLanguageHelper.getAllPackageImports(patternModel);
for (int i = 0; i < importPackages.size(); ++i) {
EPackage leftPackage = importPackages.get(i).getEPackage();
for (int j = i + 1; j < importPackages.size(); ++j) {
EPackage rightPackage = importPackages.get(j).getEPackage();
if (leftPackage.equals(rightPackage)) {
warning("Duplicate import of " + leftPackage.getNsURI(),
EMFPatternLanguagePackage.Literals.PATTERN_MODEL__IMPORT_PACKAGES, i,
EMFIssueCodes.DUPLICATE_IMPORT);
warning("Duplicate import of " + rightPackage.getNsURI(),
EMFPatternLanguagePackage.Literals.PATTERN_MODEL__IMPORT_PACKAGES, j,
EMFIssueCodes.DUPLICATE_IMPORT);
}
}
}
}
@Check
public void checkPackageImportGeneratedCode(PackageImport packageImport) {
if (packageImport.getEPackage() != null
&& packageImport.getEPackage().getNsURI() != null
&& !metamodelProvider.isGeneratedCodeAvailable(packageImport.getEPackage(), packageImport.eResource()
.getResourceSet())) {
warning(String.format(
"The generated code of the Ecore model %s cannot be found. Check the org.eclipse.emf.ecore.generated_package extension in the model project or consider setting up a generator model for the generated code to work.",
packageImport.getEPackage().getNsURI()),
EMFPatternLanguagePackage.Literals.PACKAGE_IMPORT__EPACKAGE,
EMFIssueCodes.IMPORT_WITH_GENERATEDCODE);
}
}
@Check
public void checkParametersNamed(Pattern pattern) {
for (Variable var : pattern.getParameters()) {
if (var.getName().startsWith("_")) {
error("Parameter name must not start with _", var, PatternLanguagePackage.Literals.VARIABLE__NAME,
EMFIssueCodes.SINGLEUSE_PARAMETER);
}
}
}
@Check
public void checkEnumValues(EnumValue value) {
if (value.eContainer() instanceof PathExpressionHead) {
// If container is PathExpression check for enum type assignability
EEnum enumType = value.getEnumeration();
if (enumType == null && value.getLiteral() != null) {
enumType = value.getLiteral().getEEnum();
}
PathExpressionHead expression = (PathExpressionHead) value.eContainer();
try {
EEnum expectedType = EMFPatternLanguageScopeHelper.calculateEnumerationType(expression);
if (enumType != null && !expectedType.equals(enumType)) {
error(String.format("Inconsistent enumeration types: found %s but expected %s", enumType.getName(),
expectedType.getName()), value, EMFPatternLanguagePackage.Literals.ENUM_VALUE__ENUMERATION,
EMFIssueCodes.INVALID_ENUM_LITERAL);
}
} catch (ResolutionException e) {
// EClassifier type = EMFPatternLanguageScopeHelper.calculateExpressionType(expression);
error(String.format("Invalid enumeration constant %s", enumType.getName()), value,
EMFPatternLanguagePackage.Literals.ENUM_VALUE__ENUMERATION, EMFIssueCodes.INVALID_ENUM_LITERAL);
}
}
}
/**
* The parameter's type must be the same or more specific than the type inferred from the pattern's body. This
* warning usually arises when we have more pattern bodies, which contains different type definitions for the same
* parameter. In a case like this the common parameter's type is the most specific common supertype of the
* respective calculated types in the bodies.
*
* @param pattern
*/
@Check
public void checkPatternParametersType(Pattern pattern) {
for (Variable variable : pattern.getParameters()) {
EClassifier classifierCorrect = emfTypeProvider.getClassifierForVariable(variable);
EClassifier classifierDefined = emfTypeProvider.getClassifierForType(variable.getType());
if (classifierCorrect == null || classifierDefined == null || classifierDefined.equals(classifierCorrect)) {
// Either correct - they are the same, or other validator returns the type error
return;
} else {
if (classifierCorrect instanceof EClass && classifierDefined instanceof EClass) {
if (((EClass) classifierDefined).getEAllSuperTypes().contains(classifierCorrect)) {
// Correct the defined is more specific than what the pattern needs
return;
}
}
// OK, issue warning now
warning(String.format(
"Inconsistent parameter type definition, should be %s based on the pattern definition",
classifierCorrect.getName()), variable, null, EMFIssueCodes.PARAMETER_TYPE_INVALID);
}
}
}
/**
* A variable's type can come from different sources: parameter's type definition, type definitions in the pattern
* bodies or calculated from path expression constraints or find calls. In these situations one variable might have
* conflicting type definitions. In conflicting situations if a variable's multiple types have a common subtype
* (which would ensure a pattern match runtime) and has a type defined as a parameter, than this type will be
* selected. In other cases we don't select a random type from the possibilities, the validator returns with an
* error. Note, if the multiple type definitions are related in subtype-supertype relations than the most specific
* is selected naturally (this is not even a warning).
*
* @param pattern
*/
@Check
public void checkPatternVariablesType(Pattern pattern) {
for (PatternBody patternBody : pattern.getBodies()) {
for (Variable variable : patternBody.getVariables()) {
Set<EClassifier> possibleClassifiers = emfTypeProvider.getPossibleClassifiersForVariableInBody(
patternBody, variable);
// We only need to give warnings/errors if there is more possible classifiers
if (possibleClassifiers.size() > 1) {
Set<String> classifierNamesSet = new HashSet<String>();
Set<String> classifierPackagesSet = new HashSet<String>();
for (EClassifier classifier : possibleClassifiers) {
classifierNamesSet.add(classifier.getName());
if (classifier.getEPackage() != null) {
classifierPackagesSet.add(classifier.getEPackage().getName());
}
}
// If the String sets contains only 1 elements than it is an error
// There is some element which is defined multiple types within the ecores
if (classifierNamesSet.size() == 1 && classifierPackagesSet.size() <= 1) {
error("Variable has a type which has multiple definitions: " + classifierNamesSet, variable
.getReferences().get(0), null, EMFIssueCodes.VARIABLE_TYPE_MULTIPLE_DECLARATION);
} else {
EClassifier classifier = emfTypeProvider.getClassifierForPatternParameterVariable(variable);
PatternModel patternModel = (PatternModel) patternBody.eContainer().eContainer();
if (classifier != null && possibleClassifiers.contains(classifier)
&& hasCommonSubType(patternModel, possibleClassifiers)) {
warning("Ambiguous variable type defintions: " + classifierNamesSet
+ ", the parameter type (" + classifier.getName() + ") is used now.", variable
.getReferences().get(0), null, EMFIssueCodes.VARIABLE_TYPE_INVALID_WARNING);
} else {
boolean isParameter = false;
for (Variable parameter : pattern.getParameters()) {
if (parameter.getName().equals(variable.getName())) {
isParameter = true;
}
}
if (isParameter) {
error("Ambiguous variable type defintions: "
+ classifierNamesSet
+ ", type cannot be selected. Please specify the one to be used as the parameter type"
+ " by adding it to the parameter definition.",
variable.getReferences().get(0), null,
EMFIssueCodes.VARIABLE_TYPE_INVALID_ERROR);
} else {
error("Inconsistent variable type defintions: " + classifierNamesSet
+ ", type cannot be selected.", variable.getReferences().get(0), null,
EMFIssueCodes.VARIABLE_TYPE_INVALID_ERROR);
}
}
}
}
}
}
}
/**
* @param patternModel
* @param classifiers
* @return True if the given classifiers has a common subtype. The {@link PatternModel} is needed for focusing the
* search, all ecore packages referenced from the patternmodel's head, and it's subpackages will be searched
* for common subtype elements.
*/
private boolean hasCommonSubType(PatternModel patternModel, Set<EClassifier> classifiers) {
Set<EClass> realSubTypes = new HashSet<EClass>();
Set<EClassifier> probableSubTypes = new HashSet<EClassifier>();
for (PackageImport packageImport : EMFPatternLanguageHelper.getPackageImportsIterable(patternModel)) {
probableSubTypes.addAll(getAllEClassifiers(packageImport.getEPackage()));
}
for (EClassifier classifier : probableSubTypes) {
if (classifier instanceof EClass) {
EClass eClass = (EClass) classifier;
if (eClass.getEAllSuperTypes().containsAll(classifiers)) {
realSubTypes.add(eClass);
}
}
}
return !realSubTypes.isEmpty();
}
/**
* @param ePackage
* @return all EClassifiers contained in the ePackage, and in the subpackages as well
*/
private static Set<EClassifier> getAllEClassifiers(EPackage ePackage) {
Set<EClassifier> resultSet = new HashSet<EClassifier>();
resultSet.addAll(ePackage.getEClassifiers());
for (EPackage subEPackage : ePackage.getESubpackages()) {
resultSet.addAll(subEPackage.getEClassifiers());
}
return resultSet;
}
/**
* A validator for cartesian products (isolated constraints) in pattern bodies. There are two types of warnings:
* strict and soft. Strict warning means that there are constraints in the body which has no connection at all, in
* soft cases they connected at least with a count find. The validator's result always just a warning, however a
* strict warning usually a modeling design flaw which should be corrected.
*
* @param patternBody
*/
@Check
public void checkForCartesianProduct(PatternBody patternBody) {
List<Variable> variables = patternBody.getVariables();
variables.removeAll(CorePatternLanguageHelper.getUnnamedRunningVariables(patternBody));
UnionFindForVariables justPositiveUnionFindForVariables = new UnionFindForVariables(variables);
UnionFindForVariables generalUnionFindForVariables = new UnionFindForVariables(variables);
boolean isSecondRunNeeded = false;
// First run
// Just put together the real positive connections, and all of the general connections first
for (Constraint constraint : patternBody.getConstraints()) {
Set<Variable> positiveVariables = new HashSet<Variable>();
Set<Variable> generalVariables = new HashSet<Variable>();
if (constraint instanceof CompareConstraint) {
// Equality and inequality (==, !=)
CompareConstraint compareConstraint = (CompareConstraint) constraint;
ValueReference leftValueReference = compareConstraint.getLeftOperand();
ValueReference rightValueReference = compareConstraint.getRightOperand();
Set<Variable> leftVariables = CorePatternLanguageHelper
.getVariablesFromValueReference(leftValueReference);
Set<Variable> rightVariables = CorePatternLanguageHelper
.getVariablesFromValueReference(rightValueReference);
if (CompareFeature.EQUALITY.equals(compareConstraint.getFeature())) {
// Equality ==
if (!isValueReferenceAggregated(leftValueReference)
&& !isValueReferenceAggregated(rightValueReference)) {
positiveVariables.addAll(leftVariables);
positiveVariables.addAll(rightVariables);
generalVariables.addAll(leftVariables);
generalVariables.addAll(rightVariables);
} else {
isSecondRunNeeded = true;
generalVariables.addAll(leftVariables);
generalVariables.addAll(rightVariables);
}
} else if (CompareFeature.INEQUALITY.equals(compareConstraint.getFeature())) {
// Inequality !=
generalVariables.addAll(leftVariables);
generalVariables.addAll(rightVariables);
}
} else if (constraint instanceof PatternCompositionConstraint) {
// Find and neg-find constructs
PatternCompositionConstraint patternCompositionConstraint = (PatternCompositionConstraint) constraint;
if (!patternCompositionConstraint.isNegative()) {
// Positive composition (find)
for (ValueReference valueReference : patternCompositionConstraint.getCall().getParameters()) {
if (!isValueReferenceAggregated(valueReference)) {
positiveVariables.addAll(CorePatternLanguageHelper
.getVariablesFromValueReference(valueReference));
generalVariables.addAll(CorePatternLanguageHelper
.getVariablesFromValueReference(valueReference));
} else {
isSecondRunNeeded = true;
generalVariables.addAll(CorePatternLanguageHelper
.getVariablesFromValueReference(valueReference));
}
}
} else {
// Negative composition (neg find)
for (ValueReference valueReference : patternCompositionConstraint.getCall().getParameters()) {
generalVariables.addAll(CorePatternLanguageHelper
.getVariablesFromValueReference(valueReference));
}
}
} else if (constraint instanceof PathExpressionConstraint) {
// Normal attribute-reference constraint
PathExpressionConstraint pathExpressionConstraint = (PathExpressionConstraint) constraint;
PathExpressionHead pathExpressionHead = pathExpressionConstraint.getHead();
ValueReference valueReference = pathExpressionHead.getDst();
Variable pathExpressionHeadSourceVariable = null;
if (pathExpressionHead.getSrc() != null) {
pathExpressionHeadSourceVariable = pathExpressionHead.getSrc().getVariable();
}
if (!isValueReferenceAggregated(valueReference)) {
positiveVariables.addAll(CorePatternLanguageHelper.getVariablesFromValueReference(valueReference));
positiveVariables.add(pathExpressionHeadSourceVariable);
generalVariables.addAll(CorePatternLanguageHelper.getVariablesFromValueReference(valueReference));
generalVariables.add(pathExpressionHeadSourceVariable);
} else {
isSecondRunNeeded = true;
generalVariables.addAll(CorePatternLanguageHelper.getVariablesFromValueReference(valueReference));
generalVariables.add(pathExpressionHeadSourceVariable);
}
} else if (constraint instanceof CheckConstraint) {
// Variables used together in check expression, always negative
CheckConstraint checkConstraint = (CheckConstraint) constraint;
generalVariables.addAll(CorePatternLanguageHelper
.getReferencedPatternVariablesOfXExpression(checkConstraint.getExpression()));
}
justPositiveUnionFindForVariables.unite(positiveVariables);
generalUnionFindForVariables.unite(generalVariables);
}
// Second run
// If variables in an aggregated formula (e.g.: count find Pattern(X,Y)) are in the same union in the positive
// case then they are considered to be in a positive relation with the respective target as well
// M == count find Pattern(X,Y), so M with X and Y is positive if X and Y is positive
// If the aggregated contains unnamed/running vars it should be omitted during the positive relation checking
if (isSecondRunNeeded) {
for (Constraint constraint : patternBody.getConstraints()) {
Set<Variable> positiveVariables = new HashSet<Variable>();
if (constraint instanceof CompareConstraint) {
CompareConstraint compareConstraint = (CompareConstraint) constraint;
if (CompareFeature.EQUALITY.equals(compareConstraint.getFeature())) {
// Equality (==), with aggregates in it
ValueReference leftValueReference = compareConstraint.getLeftOperand();
ValueReference rightValueReference = compareConstraint.getRightOperand();
if (isValueReferenceAggregated(leftValueReference)
|| isValueReferenceAggregated(rightValueReference)) {
Set<Variable> leftVariables = CorePatternLanguageHelper
.getVariablesFromValueReference(leftValueReference);
Set<Variable> rightVariables = CorePatternLanguageHelper
.getVariablesFromValueReference(rightValueReference);
if (justPositiveUnionFindForVariables.isSameUnion(leftVariables)) {
positiveVariables.addAll(leftVariables);
}
if (justPositiveUnionFindForVariables.isSameUnion(rightVariables)) {
positiveVariables.addAll(rightVariables);
}
}
}
} else if (constraint instanceof PatternCompositionConstraint) {
PatternCompositionConstraint patternCompositionConstraint = (PatternCompositionConstraint) constraint;
if (!patternCompositionConstraint.isNegative()) {
// Positive composition (find), with aggregates in it
for (ValueReference valueReference : patternCompositionConstraint.getCall().getParameters()) {
Set<Variable> actualVariables = CorePatternLanguageHelper
.getVariablesFromValueReference(valueReference);
if (justPositiveUnionFindForVariables.isSameUnion(actualVariables)) {
positiveVariables.addAll(actualVariables);
}
}
}
} else if (constraint instanceof PathExpressionConstraint) {
// Normal attribute-reference constraint, with aggregates in it
PathExpressionConstraint pathExpressionConstraint = (PathExpressionConstraint) constraint;
PathExpressionHead pathExpressionHead = pathExpressionConstraint.getHead();
Variable pathExpressionHeadSourceVariable = null;
if (pathExpressionHead.getSrc() != null) {
pathExpressionHeadSourceVariable = pathExpressionHead.getSrc().getVariable();
}
positiveVariables.add(pathExpressionHeadSourceVariable);
ValueReference valueReference = pathExpressionHead.getDst();
Set<Variable> actualVariables = CorePatternLanguageHelper
.getVariablesFromValueReference(valueReference);
if (justPositiveUnionFindForVariables.isSameUnion(actualVariables)) {
positiveVariables.addAll(actualVariables);
}
}
justPositiveUnionFindForVariables.unite(positiveVariables);
}
}
if (generalUnionFindForVariables.isMoreThanOneUnion()) {
// Giving strict warning in this case
warning("The pattern body contains isolated constraints (\"cartesian products\") that can lead to severe performance and memory footprint issues. The independent partitions are: "
+ generalUnionFindForVariables.getCurrentPartitionsFormatted() + ".", patternBody, null,
EMFIssueCodes.CARTESIAN_STRICT_WARNING);
} else if (justPositiveUnionFindForVariables.isMoreThanOneUnion()) {
// Giving soft warning in this case
warning("The pattern body contains constraints which are only loosely connected. This may negatively impact performance. The weakly dependent partitions are: "
+ justPositiveUnionFindForVariables.getCurrentPartitionsFormatted(), patternBody, null,
EMFIssueCodes.CARTESIAN_SOFT_WARNING);
}
}
private static boolean isValueReferenceAggregated(ValueReference valueReference) {
return valueReference instanceof AggregatedValue;
}
/**
* This validator checks if the literal or computational values match the other side's type in a compare constraint
* (equality/inequality). Both sides can be literal, we will do the check if at least on side is that.
*
* @param compareConstraint
*/
@Check
public void checkForWrongLiteralAndComputationValuesInCompareConstraints(CompareConstraint compareConstraint) {
// Equality and inequality (==, !=)
ValueReference leftValueReference = compareConstraint.getLeftOperand();
ValueReference rightValueReference = compareConstraint.getRightOperand();
if ((leftValueReference instanceof LiteralValueReference || leftValueReference instanceof ComputationValue
|| rightValueReference instanceof LiteralValueReference || rightValueReference instanceof ComputationValue)
&& !(leftValueReference instanceof VariableValue) && !(rightValueReference instanceof VariableValue)) {
EClassifier leftClassifier = EMFPatternTypeUtil
.getClassifierForLiteralComputationEnumValueReference(leftValueReference);
EClassifier rightClassifier = EMFPatternTypeUtil
.getClassifierForLiteralComputationEnumValueReference(rightValueReference);
if (!isCompatibleClassifiers(leftClassifier, rightClassifier)) {
error("The types of the literal/computational values are different: "
+ leftClassifier.getInstanceClassName() + ", " + rightClassifier.getInstanceClassName() + ".",
compareConstraint, null, EMFIssueCodes.LITERAL_OR_COMPUTATION_TYPE_MISMATCH_IN_COMPARE);
}
}
}
/**
* This validator checks if the literal or computational values match the path expression's type.
*
* @param pathExpressionConstraint
*/
@Check
public void checkForWrongLiteralAndComputationValuesInPathExpressionConstraints(
PathExpressionConstraint pathExpressionConstraint) {
// Normal attribute-reference constraint
PathExpressionHead pathExpressionHead = pathExpressionConstraint.getHead();
ValueReference valueReference = pathExpressionHead.getDst();
if (valueReference instanceof LiteralValueReference || valueReference instanceof ComputationValue) {
EClassifier inputClassifier = EMFPatternTypeUtil
.getClassifierForLiteralComputationEnumValueReference(valueReference);
EClassifier typeClassifier = EMFPatternTypeUtil.getClassifierForType(EMFPatternTypeUtil
.getTypeFromPathExpressionTail(pathExpressionHead.getTail()));
if (!isCompatibleClassifiers(typeClassifier, inputClassifier)) {
error("The type infered from the path expression (" + typeClassifier.getInstanceClassName()
+ ") is different from the input literal/computational value ("
+ inputClassifier.getInstanceClassName() + ").", pathExpressionConstraint, null,
EMFIssueCodes.LITERAL_OR_COMPUTATION_TYPE_MISMATCH_IN_PATH_EXPRESSION);
}
}
}
/**
* This validator checks if the literal or computational values match the pattern call's type.
*
* @param patternCall
*/
@Check
public void checkForWrongLiteralAndComputationValuesInPatternCalls(PatternCall patternCall) {
// Find and neg find (including count find as well)
for (ValueReference valueReference : patternCall.getParameters()) {
if (valueReference instanceof LiteralValueReference || valueReference instanceof ComputationValue) {
Pattern pattern = patternCall.getPatternRef();
Variable variable = pattern.getParameters().get(patternCall.getParameters().indexOf(valueReference));
EClassifier typeClassifier = emfTypeProvider.getClassifierForVariable(variable);
EClassifier inputClassifier = EMFPatternTypeUtil
.getClassifierForLiteralComputationEnumValueReference(valueReference);
if (!isCompatibleClassifiers(typeClassifier, inputClassifier)) {
error("The type infered from the called pattern (" + typeClassifier.getInstanceClassName()
+ ") is different from the input literal/computational value ("
+ inputClassifier.getInstanceClassName() + ").", patternCall, null,
EMFIssueCodes.LITERAL_OR_COMPUTATION_TYPE_MISMATCH_IN_PATTERN_CALL);
}
}
}
}
private static boolean isCompatibleClassifiers(EClassifier classifierFirst, EClassifier classifierSecond) {
if (classifierFirst != null && classifierSecond != null) {
Class<?> firstInstanceClass = classifierFirst.getInstanceClass();
Class<?> secondInstanceClass = classifierSecond.getInstanceClass();
if (firstInstanceClass.equals(secondInstanceClass)) {
return true;
} else if (firstInstanceClass.isPrimitive() || secondInstanceClass.isPrimitive()) {
Class<?> firstWrapperClass = getWrapperClassForType(firstInstanceClass);
Class<?> secondWrapperClass = getWrapperClassForType(secondInstanceClass);
if (firstWrapperClass.equals(secondWrapperClass)) {
return true;
}
}
}
return false;
}
/**
* @param typeClass
* @return The wrapper class if the input is primitive. If it is not, it returns with the input unchanged.
*/
private static Class<?> getWrapperClassForType(Class<?> typeClass) {
if (typeClass != null && typeClass.isPrimitive()) {
if (typeClass == boolean.class) {
return java.lang.Boolean.class;
} else if (typeClass == byte.class) {
return java.lang.Byte.class;
} else if (typeClass == char.class) {
return java.lang.Character.class;
} else if (typeClass == double.class) {
return java.lang.Double.class;
} else if (typeClass == float.class) {
return java.lang.Float.class;
} else if (typeClass == int.class) {
return java.lang.Integer.class;
} else if (typeClass == long.class) {
return java.lang.Long.class;
} else if (typeClass == short.class) {
return java.lang.Short.class;
}
}
return typeClass;
}
/**
* This validator looks up all variables in the {@link CheckConstraint} and reports an error if one them is not an
* {@link EDataType} instance. We do not allow arbitrary EMF elements in, so the checks are less likely to have
* side-effects.
*
* @param checkConstraint
*/
@Check
public void checkForWrongVariablesInXExpressions(CheckConstraint checkConstraint) {
for (Variable variable : CorePatternLanguageHelper.getReferencedPatternVariablesOfXExpression(checkConstraint
.getExpression())) {
EClassifier classifier = emfTypeProvider.getClassifierForVariable(variable);
if (classifier != null && !(classifier instanceof EDataType)) {// null-check needed, otherwise code throws
// NPE for classifier.getName()
error("Only simple EDataTypes are allowed in check expressions. The variable " + variable.getName()
+ "has a type of " + classifier.getName() + ".", checkConstraint, null,
EMFIssueCodes.CHECK_CONSTRAINT_SCALAR_VARIABLE_ERROR);
}
}
}
}